001 /* 002 * Copyright 2005-2006 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.util; 020 021 import java.rmi.RemoteException; 022 import java.rmi.server.UnicastRemoteObject; 023 import java.rmi.NoSuchObjectException; 024 import java.util.EventObject; 025 import java.util.EventListener; 026 import java.util.WeakHashMap; 027 028 /** 029 * A abstract base class that established an event queue and handles event dispatch 030 * operations for listeners declared in a class extending this base class. 031 * 032 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 033 * @version 1.0.1 034 */ 035 public abstract class UnicastEventSource extends UnicastRemoteObject implements EventHandler 036 { 037 /** 038 * Internal synchronization lock. 039 */ 040 private final Object m_lock = new Object(); 041 042 /** 043 * The controller that provides the main event dispath thread. 044 */ 045 private final EventQueue m_queue; 046 047 private final WeakHashMap m_listeners = new WeakHashMap(); 048 049 private final Logger m_logger; 050 051 /** 052 * Creation of a new <tt>UnicastEventSource</tt>. 053 * @param queue the event queue 054 * @param logger the assigned logging channel 055 * @exception RemoteException if a remote I/O exception occurs 056 */ 057 protected UnicastEventSource( EventQueue queue, Logger logger ) throws RemoteException 058 { 059 super(); 060 m_queue = queue; 061 m_logger = logger; 062 } 063 064 //-------------------------------------------------------------------------- 065 // internal 066 //-------------------------------------------------------------------------- 067 068 /** 069 * Return the logging channel assigned to the event source. 070 * @return the logging channel 071 */ 072 protected Logger getLocalLogger() 073 { 074 return m_logger; 075 } 076 077 /** 078 * Return the event queue. 079 * @return the queue 080 */ 081 protected EventQueue getEventQueue() 082 { 083 return m_queue; 084 } 085 086 /** 087 * Abstract operation to be implemented by classes extending this base class. 088 * An implementation is reposible for the posting of the event to associated 089 * listeners. Event posting will be executed under a separate thread to the 090 * thread that initiated the event post. 091 * 092 * @param event the event to process 093 */ 094 public abstract void processEvent( EventObject event ); 095 096 /** 097 * Add a listener to the set of listeners handled by this producer. 098 * @param listener the event listener 099 */ 100 protected void addListener( EventListener listener ) 101 { 102 if( null == listener ) 103 { 104 throw new NullPointerException( "listener" ); 105 } 106 synchronized( m_lock ) 107 { 108 m_listeners.put( listener, null ); 109 } 110 } 111 112 /** 113 * Remove a listener to the set of listeners handled by this producer. 114 * @param listener the event listener 115 */ 116 protected void removeListener( EventListener listener ) 117 { 118 if( null == listener ) 119 { 120 throw new NullPointerException( "listener" ); 121 } 122 synchronized( m_lock ) 123 { 124 m_listeners.remove( listener ); 125 } 126 } 127 128 129 /** 130 * Return the array of registered event listeners. 131 * 132 * @return the event listeners 133 */ 134 public EventListener[] getEventListeners() 135 { 136 synchronized( m_lock ) 137 { 138 return (EventListener[]) m_listeners.keySet().toArray( new EventListener[0] ); 139 } 140 } 141 142 /** 143 * Enqueue an event for delivery to registered 144 * listeners unless there are no registered 145 * listeners. 146 * @param event the event to enqueue 147 */ 148 protected void enqueueEvent( EventObject event ) 149 { 150 if( m_listeners.size() > 0 ) 151 { 152 m_queue.enqueueEvent( event ); 153 } 154 } 155 156 /** 157 * Return the internal synchronization lock. 158 * @return the lock object 159 */ 160 protected Object getLock() 161 { 162 return m_lock; 163 } 164 165 /** 166 * Terminate the event source. 167 */ 168 public void terminate() 169 { 170 synchronized( m_lock ) 171 { 172 EventListener[] listeners = getEventListeners(); 173 for( int i=0; i < listeners.length; i++ ) 174 { 175 EventListener listener = listeners[i]; 176 removeListener( listener ); 177 } 178 } 179 180 Thread thread = new Terminator( this, m_logger ); 181 thread.start(); 182 } 183 184 /** 185 * Internal class that handles instance retraction for the RMI runtime. 186 */ 187 private class Terminator extends Thread 188 { 189 private final UnicastEventSource m_source; 190 private final Logger m_logger; 191 192 /** 193 * Internal class terminator that handles unexport of an event source 194 * under a separate thread. 195 * @param source the event source instance 196 * @param logger the event source logger 197 */ 198 Terminator( UnicastEventSource source, Logger logger ) 199 { 200 m_source = source; 201 m_logger = logger; 202 } 203 204 /** 205 * Terminator execution. 206 */ 207 public void run() 208 { 209 try 210 { 211 UnicastRemoteObject.unexportObject( m_source, true ); 212 m_logger.debug( "terminated" ); 213 } 214 catch( NoSuchObjectException e ) 215 { 216 boolean ignoreThis = true; // objct has not been exported 217 } 218 catch( RemoteException e ) 219 { 220 final String error = 221 "Unexpected remote exception while retracting component handler remote reference."; 222 m_logger.warn( error, e ); 223 } 224 } 225 } 226 }